import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timedelta
import matplotlib.dates as mdates
import tkinter as tk
import ttkbootstrap as ttkb
from app_state import app_state
from utils import log_info, resource_path, log_debug
from services.exam_statistics_service import ExamStatisticsService
from views import CustomMessageBox
import os

class ExamStatisticsView:
    def __init__(self, font=None):
        """初始化ExamStatisticsView"""
        self.exam_statistics_service = ExamStatisticsService()
        self.font = font or ("SimHei", 12)
        # 确保中文显示正常
        plt.rcParams.update({
            'font.family': ['SimHei', 'Microsoft YaHei', 'SimSun', 'FangSong', 'KaiTi'],
            'text.color': 'white',
            'axes.labelcolor': 'white',
            'xtick.color': 'white',
            'ytick.color': 'white'
        })
        log_debug("考试统计视图初始化完成")

    def view_in_frame(self, parent_frame, user_id, root):
        """在指定框架中显示考试统计"""
        # 清空窗口
        for widget in parent_frame.winfo_children():
            widget.destroy()

        if not user_id:
            CustomMessageBox.showinfo("提示", "请先登录再查看考试统计。", parent=root, font=self.font)
            return

        # 创建统计选项框架
        options_frame = tk.Frame(parent_frame)
        options_frame.pack(pady=10, padx=20, fill=None, anchor=tk.NW)

        # 设置各列权重和uniform参数，确保宽度完全平均
        options_frame.grid_columnconfigure(0, weight=1, uniform='col')
        options_frame.grid_columnconfigure(1, weight=1, uniform='col')
        options_frame.grid_columnconfigure(2, weight=1, uniform='col')

        # 类别选择
        self.category_var = tk.StringVar(value="A")
        ttkb.Radiobutton(options_frame, text="A类", variable=self.category_var, value="A", bootstyle="primary").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        ttkb.Radiobutton(options_frame, text="B类", variable=self.category_var, value="B", bootstyle="primary").grid(row=0, column=1, padx=5, pady=5, sticky="w")
        ttkb.Radiobutton(options_frame, text="C类", variable=self.category_var, value="C", bootstyle="primary").grid(row=0, column=2, padx=5, pady=5, sticky="w")

        # 时间范围选择
        self.time_var = tk.StringVar(value="7days")
        ttkb.Radiobutton(options_frame, text="近7天", variable=self.time_var, value="7days", bootstyle="primary").grid(row=1, column=0, padx=5, pady=5, sticky="w")
        ttkb.Radiobutton(options_frame, text="近30天", variable=self.time_var, value="30days", bootstyle="primary").grid(row=1, column=1, padx=5, pady=5, sticky="w")
        ttkb.Radiobutton(options_frame, text="近半年", variable=self.time_var, value="180days", bootstyle="primary").grid(row=1, column=2, padx=5, pady=5, sticky="w")

        # 生成统计按钮
        def generate_statistics():
            # 获取选择的选项
            category = self.category_var.get()
            time_range = self.time_var.get()

            # 获取考试数据
            exam_data = self.get_exam_data(app_state.current_user_id, category, time_range)

            if not exam_data:
                CustomMessageBox.showinfo("提示", "没有找到考试数据。", parent=root, font=self.font)
                return

            # 计算统计信息
            stats_data = self.calculate_statistics(exam_data)

            # 清除旧的统计摘要和图表
            for widget in parent_frame.winfo_children():
                if widget not in [options_frame]:
                    widget.destroy()

            # 创建统计信息框架
            stats_frame = tk.Frame(parent_frame)
            stats_frame.pack(pady=10, padx=20, fill=tk.X)

            # 左侧显示统计摘要
            # 计算分:秒格式的用时
            minutes = int(stats_data['latest_duration'] / 60)
            seconds = int(stats_data['latest_duration'] % 60)
            stats_label = tk.Label(stats_frame, text=f"考试统计摘要:\n\n历史最高分: {stats_data['max_score']}\n最近一次成绩: {stats_data['latest_score']}\n最近一次用时: {minutes}分{seconds}秒\n平均通过率: {stats_data['avg_pass_rate']}%\n总考试次数: {stats_data['total_exams']}", font=self.font, justify=tk.LEFT)
            stats_label.pack(side=tk.LEFT, padx=10, fill=tk.Y)

            # 创建环形图框架，放置在界面中间靠右
            ring_chart_frame = tk.Frame(parent_frame)
            ring_chart_frame.pack(side=tk.TOP, pady=20)
            # 调整环形图位置往右往下移动
            ring_chart_frame.place(relx=0.75, rely=0.22, anchor=tk.CENTER)

            # 显示正确率环形图
            pass_rate_fig = self.plot_pass_rate_ring(exam_data)

            if pass_rate_fig:
                # 使用FigureCanvasTkAgg直接嵌入图表
                from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
                canvas = FigureCanvasTkAgg(pass_rate_fig, master=ring_chart_frame)
                pass_rate_fig.tight_layout(pad=0.5)  # pad参数控制边距
                canvas.draw()
                # 获取画布的Tkinter部件并放置
                canvas.get_tk_widget().pack()

            # 生成并显示分数趋势图(折线图)
            score_fig = self.plot_score_trend(exam_data, category, time_range)

            # 显示折线图
            if score_fig:
                # 创建图表框架，设置固定高度
                charts_frame = tk.Frame(parent_frame, height=400)  # 设置高度为200像素
                charts_frame.pack(side=tk.BOTTOM, fill=tk.X)  # 只水平填充，不垂直扩展
                charts_frame.pack_propagate(False)  # 防止框架大小被内容改变
                # 使用FigureCanvasTkAgg直接嵌入图表
                canvas = FigureCanvasTkAgg(score_fig, master=charts_frame)
                canvas.draw()
                # 获取画布的Tkinter部件并放置
                canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # 添加生成统计按钮（图片+文字）
        generate_frame = tk.Frame(options_frame, cursor="hand2")
        generate_frame.grid(row=2, column=0, columnspan=1, pady=10, sticky="w")

        generate_frame.bind("<Button-1>", lambda e: generate_statistics())

        # 加载图片
        stats_img = resource_path(os.path.join("icon", "get_statistics.png"))
        if os.path.exists(stats_img):
            from PIL import Image, ImageTk
            stats_img = Image.open(stats_img)
            stats_img = stats_img.resize((20, 20), Image.Resampling.LANCZOS)
            stats_photo = ImageTk.PhotoImage(stats_img)

            # 创建图片标签
            img_label = tk.Label(generate_frame, image=stats_photo)
            img_label.image = stats_photo
            img_label.pack(side=tk.LEFT, padx=(5, 2))
            img_label.bind("<Button-1>", lambda e: generate_statistics())

        # 创建文字标签
        text_label = tk.Label(generate_frame, text="生成统计", font=self.font)
        text_label.pack(side=tk.LEFT, padx=(2, 5))
        text_label.bind("<Button-1>", lambda e: generate_statistics())

    def get_exam_data(self, user_id: int, category: str = None, time_range: str = '7days') -> list:
        """
        获取用户的考试数据
        :param user_id: 用户ID
        :param category: 考试类别 ('A', 'B', 'C' 或 None表示全部)
        :param time_range: 时间范围 ('7days', '30days', '180days')
        :return: 考试数据列表
        """
        log_debug(f"获取用户 {user_id} 的考试数据，类别: {category}, 时间范围: {time_range}")
        return self.exam_statistics_service.get_exam_data(user_id, category, time_range)

    def calculate_statistics(self, exam_data: list) -> dict:
        """
        计算考试统计数据
        :param exam_data: 考试数据列表
        :return: 统计结果字典
        """
        return self.exam_statistics_service.calculate_statistics(exam_data)

    def plot_pass_rate_ring(self, exam_data: list) -> plt.Figure:
        """
        绘制考试正确率环形统计图
        :param exam_data: 考试数据列表
        :return: 图表对象
        """
        log_debug("开始绘制考试正确率环形图")
        if not exam_data:
            log_debug("没有考试数据，无法绘制正确率环形图")
            return None

        # 计算总的正确率
        total_correct = sum(exam['correct_questions'] for exam in exam_data)
        total_questions = sum(exam['total_questions'] for exam in exam_data)
        pass_rate = (total_correct / total_questions) * 100 if total_questions > 0 else 0

        # 创建环形图
        plt.figure(figsize=(2.5, 2.5))
        plt.gcf().set_facecolor('none')
        plt.gca().set_facecolor('none')
        labels = ['正确率', '错误率']
        sizes = [pass_rate, 100 - pass_rate]
        colors = ['#4CAF50', '#F44336']
        explode = (0.05, 0)

        plt.rcParams['font.size'] = 12
        plt.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.2f%%',
                shadow=True, startangle=90, wedgeprops={'width': 0.5}, textprops={'fontsize': 12})
        plt.axis('equal')
        plt.title('考试正确率统计', fontsize=14, y=1.05)
        plt.rcParams['font.size'] = 10

        fig = plt.gcf()
        plt.close()
        log_debug("考试正确率环形图绘制完成")
        return fig

    def plot_score_trend(self, exam_data: list, category: str = None, time_range: str = '7days') -> plt.Figure:
        """
        绘制分数趋势图
        :param exam_data: 考试数据列表
        :param category: 考试类别
        :param time_range: 时间范围
        :return: 图表对象
        """
        log_debug(f"开始绘制分数趋势图，类别: {category}, 时间范围: {time_range}")
        if not exam_data:
            log_debug("没有考试数据，无法绘制分数趋势图")
            return None

        # 创建图表
        plt.figure(figsize=(7, 3))
        plt.gcf().set_facecolor('none')
        plt.gca().set_facecolor('none')

        # 计算日期范围
        today = datetime.now().date()
        if time_range == '7days':
            min_date = today - timedelta(days=7)
            time_text = '最近7天'
        elif time_range == '30days':
            min_date = today - timedelta(days=30)
            time_text = '最近30天'
        elif time_range == '180days':
            min_date = today - timedelta(days=180)
            time_text = '最近半年'
        else:
            all_dates = [exam['exam_date'].date() for exam in exam_data]
            min_date = min(all_dates) if all_dates else today
            time_text = '所有时间'

        # 过滤数据
        filtered_data = []
        for exam in exam_data:
            exam_date = exam['exam_date'].date()
            if min_date <= exam_date <= today:
                filtered_data.append(exam)

        # 确保按日期排序
        filtered_data.sort(key=lambda x: x['exam_date'].date())

        # 设置图表属性
        scores = [exam['score'] for exam in filtered_data]
        min_score = 0
        max_score = 40
        if category == 'A':
            max_score = 40
        elif category == 'B':
            max_score = 60
        elif category == 'C':
            max_score = 90

        plt.yticks(np.arange(min_score, max_score+1, step=10))
        plt.ylim(bottom=0)
        title = '考试成绩趋势图'
        title += f' ({time_text})'
        plt.title(title)
        plt.xlabel('考试日期')
        plt.ylabel('成绩 (分)')
        plt.grid(True, linestyle='--', alpha=0.7)

        # 设置X轴日期刻度
        if time_range == '7days':
            date_range = [today - timedelta(days=i) for i in range(7, -1, -1)]
        elif time_range == '30days':
            date_diff = (today - min_date).days
            step = 5
            date_range = [min_date + timedelta(days=i) for i in range(0, date_diff+1, step)]
            if today not in date_range:
                date_range.append(today)
                date_range.sort()
        elif time_range == '180days':
            date_diff = (today - min_date).days
            step = 30
            date_range = [min_date + timedelta(days=i) for i in range(0, date_diff+1, step)]
            if today not in date_range:
                date_range.append(today)
                date_range.sort()
        else:
            all_dates = [exam['exam_date'].date() for exam in exam_data]
            min_date = min(all_dates) if all_dates else today
            date_diff = (today - min_date).days
            if date_diff <= 7:
                step = 1
                date_range = [min_date + timedelta(days=i) for i in range(0, date_diff+1, step)]
            elif date_diff <= 30:
                step = 5
                date_range = [min_date + timedelta(days=i) for i in range(0, date_diff+1, step)]
            else:
                step = 30
                date_range = [min_date + timedelta(days=i) for i in range(0, date_diff+1, step)]
            if today not in date_range:
                date_range.append(today)
                date_range.sort()

        date_labels = [date.strftime('%Y-%m-%d') for date in date_range]
        date_range_num = mdates.date2num(date_range)

        # 绘制数据
        if category:
            cat_data = [exam for exam in filtered_data if exam['category'] == category]
            dates_num = mdates.date2num([exam['exam_date'] for exam in cat_data])
            scores = [exam['score'] for exam in cat_data]

            # 处理同一天多次考试的情况
            unique_dates = {d: [] for d in set(date.strftime('%Y-%m-%d') for date in [exam['exam_date'] for exam in cat_data])}
            for i, date in enumerate([exam['exam_date'] for exam in cat_data]):
                date_str = date.strftime('%Y-%m-%d')
                unique_dates[date_str].append(i)

            # 应用偏移
            offset_dates_num = []
            for i, date_num in enumerate(dates_num):
                date_str = [exam['exam_date'] for exam in cat_data][i].strftime('%Y-%m-%d')
                if len(unique_dates[date_str]) > 1:
                    index = unique_dates[date_str].index(i)
                    total = len(unique_dates[date_str])
                    offset = (index - (total - 1) / 2) * 0.05
                    offset_dates_num.append(date_num + offset)
                else:
                    offset_dates_num.append(date_num)

            plt.plot(offset_dates_num, scores, 'o-', label=f'{category}类')
            for x, y in zip(offset_dates_num, scores):
                plt.annotate(f'{int(y)}', xy=(x, y), xytext=(0, 5), textcoords='offset points', ha='center')
        else:
            categories = set(exam['category'] for exam in filtered_data)
            for cat in categories:
                cat_data = [exam for exam in filtered_data if exam['category'] == cat]
                dates = [exam['exam_date'] for exam in cat_data]
                dates_num = mdates.date2num(dates)
                scores = [exam['score'] for exam in cat_data]

                # 处理同一天多次考试的情况
                unique_dates = {d: [] for d in set(date.strftime('%Y-%m-%d') for date in dates)}
                for i, date in enumerate(dates):
                    date_str = date.strftime('%Y-%m-%d')
                    unique_dates[date_str].append(i)

                # 应用偏移
                offset_dates_num = []
                for i, date_num in enumerate(dates_num):
                    date_str = dates[i].strftime('%Y-%m-%d')
                    if len(unique_dates[date_str]) > 1:
                        index = unique_dates[date_str].index(i)
                        total = len(unique_dates[date_str])
                        offset = (index - (total - 1) / 2) * 0.05
                        offset_dates_num.append(date_num + offset)
                    else:
                        offset_dates_num.append(date_num)

                plt.plot(offset_dates_num, scores, 'o-', label=f'{cat}类')
                for x, y in zip(offset_dates_num, scores):
                    plt.annotate(f'{int(y)}', xy=(x, y), xytext=(0, 5), textcoords='offset points', ha='center')

        plt.legend(labelcolor='darkblue')

        # 设置X轴刻度
        ax = plt.gca()
        x_min, x_max = min(date_range_num), max(date_range_num)
        num_ticks = min(len(date_range_num), 10)

        if num_ticks < 2:
            x_diff = max(1, (x_max - x_min) * 0.2)
            x_min -= x_diff
            x_max += x_diff
            ax.set_xlim(x_min, x_max)
            tick_positions = [x_min, (x_min + x_max) / 2, x_max]
            tick_labels = []
            for pos in tick_positions:
                closest_date = min(date_range_num, key=lambda x: abs(x - pos))
                idx = list(date_range_num).index(closest_date)
                tick_labels.append(date_labels[idx])
            plt.xticks(tick_positions, tick_labels, rotation=45)
        else:
            ax.xaxis.set_major_locator(plt.FixedLocator(date_range_num))
            ax.xaxis.set_minor_locator(plt.NullLocator())
            plt.xticks(date_range_num, date_labels, rotation=45)

        ax.set_xlim(x_min - (x_max - x_min) * 0.05, x_max + (x_max - x_min) * 0.05)
        plt.tight_layout()

        fig = plt.gcf()
        plt.close()
        log_debug("分数趋势图绘制完成")
        return fig
